package eval.host;

import java.util.ArrayList;
import java.util.List;

import eval.exceptions.CpuLeakAtHostException;
import eval.exceptions.VmNotFoundException;
import eval.utilities.configuration.ExperimentConfiguration;
import eval.vm.VirtualMachine;

public class Host {
	private static ExperimentConfiguration configuration = ExperimentConfiguration
			.getSingleton();
	
	
	
	private List<VirtualMachine> vmAtHost;
	private final int hostIdentifier;
	private HostCapacityEnum hostCapacityEnum;
	private HostCapacity hostCapacity;
	private boolean open;
	private boolean candidateForClosing;
	private int tickRemainingForClosing;
	private int tickHostTurnOn;
	private int currentCpuLoad;
	private int currentMemLoad;
	private int currentDemand;
	
	private double RedLine;
	private double MaxLine;
	private double MinLine;

	public Host(HostCapacityEnum hostCapacityEnum,HostCapacity hostCapacity,int hostIdentifier ) {
		this.hostIdentifier = hostIdentifier;
		vmAtHost = new ArrayList<VirtualMachine>();
		this.hostCapacityEnum = hostCapacityEnum;
		this.hostCapacity = hostCapacity;
		this.open = false;
		this.candidateForClosing = false;
		this.tickRemainingForClosing = -1;
		this.tickHostTurnOn= -1;
		this.currentCpuLoad = 0;
		this.currentMemLoad = 0;
		this.currentDemand = 0;
		this.RedLine = configuration.HostRedLine ;
		this.MaxLine = configuration.MaxLine ;
		this.MinLine = configuration.MinLine ;
	}

	public int getHostId() {
		return hostIdentifier;
	}

	public HostCapacityEnum getHostCapacityEnum() {
		return hostCapacityEnum;
	}
	
	public HostCapacity getHostCapacity() {
		return hostCapacity;
	}
	
	public boolean isOpen() {
		return open;
	}
	
	public boolean isCandidateForClosing() {
		return candidateForClosing;
	}
	
	public int getTickRemainingForClosing() {
		return tickRemainingForClosing;
	}
	
	public int getCurrentMemLoad(){
		return currentMemLoad;
	}
	
	public int getCurrentCpuLoad(){
		return currentCpuLoad;
	}
	
	public int getCurrentDemand(){
		return currentDemand;
	}

	public boolean isClose() {
		return (!this.open);
	}
	
	public void open(int tick) {
		this.open = true;
		this.candidateForClosing = false;
		this.tickRemainingForClosing = -1;
		this.tickHostTurnOn= tick;
		this.vmAtHost.clear();
		

	}
	
	public int getTickHostTurnOn() {
		return tickHostTurnOn;
	}

	protected void close() {
		this.open = false;
		this.candidateForClosing = false;
		this.tickRemainingForClosing = -1;
		this.vmAtHost.clear();
		currentCpuLoad = 0;
		currentMemLoad = 0;
		currentDemand = 0;
		this.tickHostTurnOn = -1;
	}

	protected void makeCandidateForClosing(int tickRemainingForClosing) {
		this.candidateForClosing = true;
		this.tickRemainingForClosing = tickRemainingForClosing;
	}
	
	protected void remCandidateForClosing() {
		//TODO exception
		if(this.candidateForClosing) {
			this.candidateForClosing = false;
			this.tickRemainingForClosing = -1;
		}
	}
	
	protected void setCurrentCpuLoad(int cpuLoad){
		currentCpuLoad = cpuLoad;
	}
	
	protected void setCurrentMemLoad(int memLoad){
		currentMemLoad = memLoad;
	}
	
	protected void updateCurrentCpuLoad(int cpuLoad){
//		if(this.hostCapacity.getCpuCapacity()<currentCpuLoad+cpuLoad)
//			throw new CpuLeakAtHostException("Host number: "+hostIdentifier+" cpu leak, after update: "+(currentCpuLoad+cpuLoad));
		currentCpuLoad = currentCpuLoad+cpuLoad;
	}
	
	protected void updateCurrentMemLoad(int memLoad){
		currentMemLoad = currentMemLoad+memLoad;
	}
	
	protected void setCurrentCpuDemand(int cpuDemand){
		currentDemand = cpuDemand;
	}
	
	protected void addVmToHost(VirtualMachine vm,int cpuLoad,int memLoad ) {
		if(!vmAtHost.contains(vm)) {
			vmAtHost.add(vm);
			updateCurrentCpuLoad(cpuLoad);
			updateCurrentMemLoad(memLoad);
		}
		
	}
	
//	protected void addVmToHost(VirtualMachine vm) {
//		if(!vmAtHost.contains(vm))
//			vmAtHost.add(vm);
//	}
	
	protected List<VirtualMachine> getVmsAtHost () {
		return vmAtHost;
	}
	
	
	protected void removeListOfVmFromHost(List<VirtualMachine> removeList) {
		for(VirtualMachine vm : removeList) {
			removeVmFromHost(vm);
		}
	}
	protected void removeVmFromHost(VirtualMachine vm) {
		if(vmAtHost.contains(vm)) {
			int cpuTorem = vm.getSatisfiedCpuDemand();
			int memTorem = vm.getSatisfiedMemDemand();
			updateCurrentCpuLoad(-cpuTorem);
			updateCurrentMemLoad(-memTorem);
			vmAtHost.remove(vm);
		}
	}
	
	@Override
	public String toString() {
		return "[Host, ID: " + hostIdentifier + ", CAPACITY: "
				+ hostCapacityEnum + "]";
	}

	@Override
	public int hashCode() {
		final int prime = 31;
		int result = 1;
		result = prime * result + hostIdentifier;
		return result;
	}

	@Override
	public boolean equals(Object obj) {
		if (this == obj)
			return true;
		if (obj == null)
			return false;
		if (getClass() != obj.getClass())
			return false;
		Host other = (Host) obj;
		if (hostIdentifier != other.hostIdentifier)
			return false;
		return true;
	}
}
